package trees.parsers
import trees.settings.Settings
import trees.tree.{AbstractTree, TreeBuilder}
import scala.io.Source._


object NexusParser {

        def writeNexusFileWithWeights(fileName:String, trees:List[AbstractTree], weights:List[int]){
            val fw = new java.io.FileWriter(fileName) 
            fw.write("#NEXUS\n") 
            fw.write("Begin trees;\n")
            var treeString = ""
            for (i <- 0 until trees.size)
                fw.write("tree " +trees(i).name +" = " +"[&W 1/" +weights(i).toString() +"] " +trees(i).toString +"\n")
            fw.write("end;\n")
            fw.close()
        }

        def writeNexusFile(fileName:String, trees:List[AbstractTree]){
            val fw = new java.io.FileWriter(fileName) 
            fw.write("#NEXUS\n") 
            fw.write("Begin trees;\n")
            for (tree <- trees){
                fw.write("tree "+ tree.name +" = " +tree.toString +"\n")
            }
            fw.write("end;\n")
            fw.close()
        }

        def getImmutableTreesFromFile(fileName:String):List[AbstractTree] = {
            readNexusTreeStringsFromFile(fileName, true)
        }

        def getMutableTreesFromFile(fileName:String):List[AbstractTree] = {
            readNexusTreeStringsFromFile(fileName, false)
        }
	//Reads a translation mapping and tree data from a nexus file
	//The translating is used to translate the numbers often used in nexus tree strings
	//back into taxnames for further analysis later on.
        //Returned is a List[TreeData] were each TreeData has info on one tree
	private def readNexusTreeStringsFromFile(fileName: String, immutable:boolean):List[AbstractTree] = {
                val lines = fromFile(fileName).getLines
                var treeList = List[AbstractTree]()
		var translation = List[(int, String)]()
                var translationMap = Map.empty[Int, String]
                var strippedLine = ""
		var translate = false
                var index = 0
                for (line <- lines){
                    strippedLine = line.replaceAll("^[ \t]+|[ \t\r\n]+$", "")
                    if (strippedLine.length > 0){
                        if (translate){
                            if (strippedLine.length > 1){
                                var t = strippedLine.replaceAll(";", "").split(" ")
                                translation = translation.::((t(0).toInt, t(1).replaceAll(",", "")))
                                if (strippedLine.endsWith(";")){
                                    translate = false
                                    translationMap = Map.empty[Int, String] ++ translation
                                }
                            }else{
                                if (strippedLine.endsWith(";")){
                                    translate = false
                                    translationMap = Map.empty[Int, String] ++ translation
                                }
                            }

                        }
                        if (strippedLine.toLowerCase().startsWith("translate")){
                            translate = true
                        }
                        if (strippedLine.toLowerCase().startsWith("tree")){
                            treeList = TreeBuilder.buildTree(parseNexusTreeData(strippedLine, translationMap), immutable) :: treeList
                            index += 1
                            if (Settings.verbose >= 2){
                                if (index % 1000 == 0){
                                    print(".")
                                }
                                if (index % 10000 == 0){
                                    println()
                                }
                            }
			}
                   }
               }
                if (Settings.verbose >= 2){
                    println()
                }
                treeList
	}

	//Parses a tree line from a nexus file and returns a TreeData with all info
	private def parseNexusTreeData(treeLine: String, translationMap: Map[Int, String]) = {
		var nameFound = false
		var name = ""
		var rooted = true
		var weight = 1.0
		var treeString = ""
		var names = List[String]()
		for (word <- treeLine.replaceAll("tree ", "").replaceAll("\\[&W 1","\\[&W=1").split(" ")){
			if (! nameFound){
				name = word
				nameFound = true
			}
			if (word.startsWith("[&U")){
				rooted = false
			}
			if (word.startsWith("[&W")){
				if (word.contains("/")){
					var t = word.split("/")
					weight = 1.0/(t(1).replaceAll("]","").toInt)
				}
			}
			if (word.startsWith("(")){ 
				if (! translationMap.isEmpty ){
					val (t,n) = translateTreeString(word, translationMap)
					treeString = t
					names = n
				}else{
					treeString = word
				}
			}
		}
		TreeData(name, treeString, names, weight, rooted)
	} 
	
	//translates the numbers commonly used in nexus treestrings to the correct taxa names
	//using the translation table from the nexus file
	private def translateTreeString(treeString: String, translationMap: Map[Int, String]) = {
		var names = List[String]()
		var translatedTree = ""
		var number = 0
                var colon = false
		for (char <- treeString){
			if (char == '(' || char == ';'){
				translatedTree += char
			}else{
				if (char == ')' || char == ','){
                                        colon = false
				  	if (number > 0 ){
				  		translatedTree += translationMap(number)
				  		names = names.::(translationMap(number))
				  		number = 0
				  	}
					translatedTree += char
                                        
				}
				else{
                                    if (char  == ':'){
                                        colon = true
                                    }else{
                                        if (!colon ){
                                            if (number > 0){
						number = number*10 + char.toString.toInt
                                            }
                                            else{
						number = char.toString.toInt
                                            }
                                        }
                                    }
				}
			}
		}
		(translatedTree, names)
	}

}
